package com.xyy.saas.payment.manager;

import com.alibaba.fastjson.JSONObject;
import com.xyy.framework.redis.core.RedisClient;
import com.xyy.saas.payment.adpater.yzh.constant.ConfigPath;
import com.xyy.saas.payment.adpater.yzh.constant.DataDict;
import com.xyy.saas.payment.adpater.yzh.vo.*;
import com.xyy.saas.payment.callback.core.dto.CallbackMsg;
import com.xyy.saas.payment.callback.core.result.CallbackRunner;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.common.RocketMqTopic;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.cores.bo.YzhwxPayBo;
import com.xyy.saas.payment.cores.enums.ResultCodeEnum;
import com.xyy.saas.payment.cores.enums.TransactionStatusEnum;
import com.xyy.saas.payment.cores.vo.*;
import com.xyy.saas.payment.dao.model.WXPayRecord;
import com.xyy.saas.payment.service.WXPayRecordService;
import com.xyy.saas.payment.util.*;
import com.xyy.saas.payment.util.mq.MqProducer;
import com.xyy.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.rocketmq.client.producer.SendResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Objects;

@Slf4j
@Service
public class WXPayManager {
    @Autowired
    private LeafUtil leafUtil;
    @Autowired
    private RedisClient redisClient;
    @Autowired
    private MqProducer mqProducer;
    @Autowired
    private WXPayRecordService wxPayRecordService;
    @Autowired
    private CallbackRunner callbackRunner;
    @Autowired
    private BankPayManager bankPayManager;

    /**
     * 微信企业付款到零钱
     * @param yzhwxPayVo
     * @param sign
     * @return
     */
    public ResultVO<YzhwxPayBo> wxPay(YzhwxPayVo yzhwxPayVo, String sign) throws PaymentException {
        log.info("WXPayManager#wxPay，yzhwxPayVo={}, sign={}", JSONUtils.toJSON(yzhwxPayVo), sign);

        // 1.参数校验
        checkWXPayParam(yzhwxPayVo);

        // 2.并发请求校验
        if (!StringUtil.isEmpty(redisClient.get(yzhwxPayVo.getBusinessOrderNo()))){
            return ResultVO.create(ResultCodeEnum.NOT_CONTINUOUS_CLICK);
        }

        try {
            redisClient.set(yzhwxPayVo.getBusinessOrderNo(), "1", 5L);
        }
        catch(Exception ex) {
            log.error("key=" + yzhwxPayVo.getBusinessOrderNo() + ", msg=" + ex.getMessage(), ex);
            return ResultVO.createError("加锁处理失败，稍后重试");
        }

        WXPayRecord wxPayRecord = wxPayRecordService.queryByBusinessNo(yzhwxPayVo.getBusinessOrderNo());

        // 3.幂等校验
        idempotentCheck(wxPayRecord);

        String payNo;

        // 4.插入记录
        if(wxPayRecord == null) {
            payNo = leafUtil.getSnowflakeId();
            wxPayRecord = build(yzhwxPayVo, payNo);
            wxPayRecordService.save(wxPayRecord);
        }
        else {
            // 1.本地状态失败可原单号重试
            /**
             * 失败：可重试
             * 挂单：不可重试，需等渠道终态
             * 取消：不可重试，需换业务单号
             */
            if(Integer.parseInt(TransactionStatusEnum.FAIL.getCode()) != wxPayRecord.getStatus()) {
                throw new PaymentException(ResultCodeEnum.REPEAT_REQUEST);
            }

            payNo = wxPayRecord.getPayNo();
            BackNotifyData backNotifyData = bankPayManager.queryPayResultFromYzh(payNo, ConfigPath.HY_WX_PAY);
            log.info("WXPayManager#queryPayResultFromYzh backNotifyData={}", JSONUtils.toJSON(backNotifyData));

            // 2.渠道失败需换单号重试
            if(backNotifyData != null && TransactionStatusEnum.FAIL == transferStatus(backNotifyData.getStatus())) {
                payNo = ConcatStringUtil.genNo(payNo);
            }

            wxPayRecord.setStatus(Integer.parseInt(TransactionStatusEnum.TRANSACTING.getCode()));
            wxPayRecord.setPayNo(payNo);
            wxPayRecord.setOpenid(yzhwxPayVo.getOpenid());
            wxPayRecord.setRealName(yzhwxPayVo.getRealName());
            wxPayRecord.setIdCard(yzhwxPayVo.getIdCard());
            wxPayRecord.setAmount(yzhwxPayVo.getAmount().longValue());
            wxPayRecord.setRemark(StringUtils.isNotBlank(yzhwxPayVo.getRemark()) ? yzhwxPayVo.getRemark() : TransactionStatusEnum.TRANSACTING.getDesc());
            wxPayRecordService.updateById(wxPayRecord);
        }

        // 5.异步调用
        SendResult sendResult = mqProducer.syncSendMsg(RocketMqTopic.TOPIC_ASYNC_WX_PAY, payNo);
        log.info("WXPayManager#wxPay send mq(topic_async_wx_pay) result:{}, param:{}", JSONUtils.toJSON(sendResult), payNo);

        // 6.返回结果
        return ResultVO.createSuccess(
                YzhwxPayBo.builder().
                        payNo(payNo).
                        businessOrderNo(yzhwxPayVo.getBusinessOrderNo()).
                        amount(yzhwxPayVo.getAmount()).
                        status(TransactionStatusEnum.TRANSACTING).
                        build());
    }

    /**
     * 处理回调消息
     * @param backNotifyMessage
     * @return
     */
    public boolean dealNotify(BackNotifyMessage backNotifyMessage) throws PaymentException {
        log.info("WXPayManager#dealNotify notifyMessage={}", JSONObject.toJSONString(backNotifyMessage));
        BackNotifyData callBackResult = backNotifyMessage.getData();

        // 1.参数校验
        if(callBackResult == null) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "data为空");
        }

        if(StringUtils.isBlank(callBackResult.getOrder_id())) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "Order_id为空");
        }

        if(StringUtils.isBlank(callBackResult.getStatus())) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "status为空");
        }

        WXPayRecord wxPayRecord = wxPayRecordService.queryByPayNo(callBackResult.getOrder_id());

        if(wxPayRecord == null) {
            log.error("WXPayManager#dealNotify 未查到单据 payNo:{}", callBackResult.getOrder_id());
            return false;
        }

        // 2.幂等校验
        TransactionStatusEnum status = transferStatus(callBackResult.getStatus());

        // 状态无变化，不处理
        if(Integer.parseInt(status.getCode()) == wxPayRecord.getStatus()) {
            return true;
        }

        // 3.更新状态
        wxPayRecord.setStatus(Integer.parseInt(status.getCode()));

        if(StringUtils.isNotBlank(callBackResult.getFinished_time())) {
            try {
                wxPayRecord.setFinishedTime(LocalDateTime.parse(callBackResult.getFinished_time(),
                        DateTimeFormatter.ofPattern(DateUtil.DATEFORMATSECOND)));
            }
            catch(Exception ex) {
                log.error("WXPayManager#dealNotify 解析完成时间失败" + ex.getMessage(), ex);
                wxPayRecord.setFinishedTime(LocalDateTime.now());
            }
        }

        wxPayRecord.setRemark(getRemark(callBackResult));

        YzhBankPayCallbackExtraVo extraVo = new YzhBankPayCallbackExtraVo(
                AmountCastUtil.castY2F(callBackResult.getBroker_fee()),
                AmountCastUtil.castY2F(callBackResult.getBroker_real_fee()),
                AmountCastUtil.castY2F(callBackResult.getBroker_deduct_fee()
                ));
        wxPayRecord.setExtra(JSONUtils.toJSON(extraVo));
        wxPayRecordService.updateByPayNo(wxPayRecord);

        // 4.通知业务，由于中间态不能重试，只更新支付平台状态，不通知业务方
        if(status != TransactionStatusEnum.HANGING_ORDER) {
            String type = (status == TransactionStatusEnum.SUCCESS) ? CallbackResultVo.TYPE_WX_PAY_SUCCESS : CallbackResultVo.TYPE_WX_PAY_FAIL;
            wxPayCallback(type, wxPayRecord.getCallback(), wxPayRecord.getPayNo(),
                    wxPayRecord.getBusinessNo(), wxPayRecord.getRemark(), wxPayRecord.getFinishedTime());
        }

        return true;
    }

    /**
     * 更新三方数据
     * @param wxPayOrderResp
     */
    public void updateWXPayStatus(WXPayOrderResp wxPayOrderResp) {
        log.info("WXPayManager#updateWXPayStatus wxPayOrderResp:{}", JSONUtils.toJSON(wxPayOrderResp));

        if(wxPayOrderResp == null || StringUtils.isBlank(wxPayOrderResp.getOrder_id())) {
            return;
        }

        WXPayRecord wxPayRecord = new WXPayRecord();
        wxPayRecord.setPayNo(wxPayOrderResp.getOrder_id());
        wxPayRecord.setRefNo(wxPayOrderResp.getRef());
        wxPayRecord.setRemark(wxPayOrderResp.getFailMsg());

        if(!DataDict.SUCCESS_CODE.equals(wxPayOrderResp.getCode())) {
            wxPayRecord.setStatus(Integer.valueOf(TransactionStatusEnum.FAIL.getCode()));
        }

        log.info("WXPayManager#updateWXPayStatus wxPayRecord:{}", JSONUtils.toJSON(wxPayRecord));
        wxPayRecordService.updateByPayNo(wxPayRecord);
    }

    /**
     * 回调业务
     * @param type
     * @param callBack
     * @param payNo
     * @param businessOrderNo
     * @param message
     * @param finishTime
     */
    public void wxPayCallback(String type, String callBack, String payNo, String businessOrderNo, String message, LocalDateTime finishTime) {
        callbackRunner.callback(CallbackMsg.build(
                type,
                callBack,
                payNo,
                businessOrderNo,
                message,
                //完成时间，时间戳转换
                null != finishTime ?
                        finishTime.toInstant(ZoneOffset.of("+8")).toEpochMilli() :
                        System.currentTimeMillis()));
    }

    /**
     * 检验位置付款到零钱参数
     * @param param
     */
    private void checkWXPayParam(YzhwxPayVo param) throws PaymentException {
        if(param == null) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR);
        }

        if(param.getBusinessType() == null) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "businessType不能为空");
        }

        if(StringUtils.isBlank(param.getOpenid())) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "openid不能为空");
        }

        if(StringUtils.isBlank(param.getBusinessOrderNo())) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "businessOrderNo不能为空");
        }

        if(StringUtils.isBlank(param.getRealName())) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "reaalName不能为空");
        }

        if(StringUtils.isBlank(param.getIdCard())) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "idCard不能为空");
        }

        if(param.getAmount() == null || param.getAmount() <= NumberUtils.INTEGER_ZERO) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "amount不能小于0");
        }

        if(StringUtils.isBlank(param.getCallback())) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "callBack不能为空");
        }

        if(StringUtils.isNotBlank(param.getRemark()) && param.getRemark().length() > Constants.MAX_STRING_LENGTH_PRODUCT_NAME) {
            throw new PaymentException(ResultCodeEnum.PARAM_ERROR, "备注信息不得超过20个字符");
        }
    }

    /**
     * 幂等校验
     * @param wxPayRecord
     * @throws PaymentException
     */
    private void idempotentCheck(WXPayRecord wxPayRecord) throws PaymentException {
        if(wxPayRecord == null) {
            return;
        }

        switch(Objects.requireNonNull(TransactionStatusEnum.code(wxPayRecord.getStatus()))) {
            case TRANSACTING:
            case SUCCESS:
                throw new PaymentException(ResultCodeEnum.REPEAT_REQUEST);
        }
    }

    /**
     * 构建对象
     * @param yzhwxPayVo
     * @param payNo
     * @return
     */
    private WXPayRecord build(YzhwxPayVo yzhwxPayVo, String payNo) {
        WXPayRecord record = new WXPayRecord();
        record.setBusinessType(yzhwxPayVo.getBusinessType().getType());
        record.setBusinessNo(yzhwxPayVo.getBusinessOrderNo());
        record.setPayNo(payNo);
        record.setAmount(yzhwxPayVo.getAmount().longValue());
        record.setRealName(yzhwxPayVo.getRealName());
        record.setIdCard(yzhwxPayVo.getIdCard());
        record.setStatus(Integer.parseInt(TransactionStatusEnum.TRANSACTING.getCode()));
        record.setRemark(StringUtils.isNotBlank(yzhwxPayVo.getRemark()) ? yzhwxPayVo.getRemark() : TransactionStatusEnum.TRANSACTING.getDesc());
        record.setPayChannel(yzhwxPayVo.getPayChannel().getCode());
        record.setMerchantId(ApolloUtil.getProperty(ConfigPath.WX_YZH_DEALERID));
        record.setOpenid(yzhwxPayVo.getOpenid());
        record.setCallback(yzhwxPayVo.getCallback());
        return record;
    }

    /**
     * 状态转换
     * @param status
     * @return
     */
    private TransactionStatusEnum transferStatus(String status) {
        if("1".equals(status)) {
            return TransactionStatusEnum.SUCCESS;
        }
        else if("2".equals(status)) {
            return TransactionStatusEnum.FAIL;
        }
        else if("4".equals(status)) {
            return TransactionStatusEnum.HANGING_ORDER;
        }
        else if("15".equals(status)) {
            return TransactionStatusEnum.CANCEL;
        }

        throw new UnsupportedOperationException("Unsupported status:" + status);
    }

    private String getRemark(BackNotifyData callBackResult) {
        StringBuilder remark = new StringBuilder();

//        if(StringUtils.isNotBlank(callBackResult.getPay_remark())) {
//            if(remark.length() > NumberUtils.INTEGER_ZERO) {
//                remark.append(",");
//            }
//
//            remark.append(callBackResult.getPay_remark());
//        }

        if(StringUtils.isNotBlank(callBackResult.getStatus_message())) {
            if(remark.length() > NumberUtils.INTEGER_ZERO) {
                remark.append(",");
            }

            remark.append(callBackResult.getStatus_message());
        }

        if(StringUtils.isNotBlank(callBackResult.getStatus_detail_message())) {
            if(remark.length() > NumberUtils.INTEGER_ZERO) {
                remark.append(",");
            }

            remark.append(callBackResult.getStatus_detail_message());
        }

        return remark.toString();
    }
}
